/* Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *  * Neither the name of NVIDIA CORPORATION nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.nvidia.fcamerapro;


import java.io.File;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Pattern;



import Jama.Matrix;
import Jama.SingularValueDecomposition;
import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.Activity;
import android.app.DialogFragment;
import android.app.FragmentTransaction;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

/**
 * Main android activity. Apart from handling the application life-cycle, the
 * code creates and controls switching between different fragments (viewer, mono
 * camera, stereo camera).
 */
public final class FCameraPROActivity extends Activity implements ActionBar.TabListener {

    /**
     * Fragment tag enumeration
     */
    enum Fragments {
        /**
         * Camera fragment (mono image capture)
         */
        FRAGMENT_CAPTURE_MONO,
        /**
         * Camera fragment (stereo image capture)
         */
        FRAGMENT_CAPTURE_STEREO,
        /**
         * Image viewer fragment
         */
        FRAGMENT_VIEWER
    };

    /**
     * Reference to mono camera view fragment
     */
    private CameraFragment mCameraFragment;
    /**
     * Reference to stereo camera view fragment
     */
    private StereoCameraFragment mStereoCameraFragment;
    /**
     * Reference to the image viewer
     */
    private ViewerFragment mViewerFragment;

    /**
     * Absolute path to image storage directory
     */
    private String mStorageDirectory;

    /**
     * Returns the absolute path to image storage directory
     *
     * @return absolute path to image storage directory
     */
    public String getStorageDirectory() {
        return mStorageDirectory;
    }

    /**
     * Called when activity stops execution (goes background). Currently the
     * native FCam implementation does not support running background so we need
     * to kill the application.
     */
    @Override
    public void onStop() {
        // XXX: hack to kill background app instance
        System.exit(0);
    }

    /**
     * Called when activity starts. Gets (or creates if it does not exist) the
     * storage directory, enumerates next free file image id, creates camera and
     * viewer fragments and puts them into tabs.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //NATIVE CODE TEST 1
        //int fibo = Fibonacci( 10 );
        //Log.d("DEBUG","Fibonacci value: "+ fibo);
        // create storage dir
        try {
            mStorageDirectory = Environment.getExternalStorageDirectory().getCanonicalPath() + File.separatorChar
                                + Settings.STORAGE_DIRECTORY;
            Log.d("DEBUG", "Storage Directory location "+mStorageDirectory);
            // string: /mnt/sdcard/DCIM/fcam
            File dir = new File(mStorageDirectory);
            if (!dir.exists()) {
                if (!dir.mkdirs()) {
                    throw new IOException("Unable to create gallery storage!");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // pass the storage location to the native code
        FCamInterface.GetInstance().setStorageDirectory(mStorageDirectory);

        // figure out first available stack id
        File dir = new File(mStorageDirectory); //generates a file for the image in the storage location
        final Pattern pattern = Pattern.compile(Settings.IMAGE_STACK_PATTERN); //"img---.xml" is the pattern for image file name generation
        String[] stackFileNames = dir.list(new FilenameFilter() {
            public boolean accept(File dir, String filename) {
                if (pattern.matcher(filename).matches()) {
                    return true;
                }
                return false;
            }
        });

        if (stackFileNames != null && stackFileNames.length > 0) {
            Arrays.sort(stackFileNames);
            // search
            int startIndex = Settings.IMAGE_STACK_PATTERN.indexOf("\\d");
            // XXX: assuming stack id will *always* have 4 digits
            int lastId = Integer.parseInt(stackFileNames[stackFileNames.length - 1].substring(startIndex, startIndex + 4));
            FCamInterface.GetInstance().setOutputFileId(lastId + 1);
        }

        // we keep a single instance of each fragment despite Android
        // Fragment API seems to destroy the fragment after each tab switch
        mCameraFragment = new CameraFragment();
        mViewerFragment = new ViewerFragment();
        mStereoCameraFragment = new StereoCameraFragment();

        setContentView(R.layout.main);

        // full-screen
        // requestWindowFeature(Window.FEATURE_NO_TITLE);
        // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
        // WindowManager.LayoutParams.FLAG_FULLSCREEN);

        // add tabs
        ActionBar bar = getActionBar();
        bar.addTab(bar.newTab().setText(getResources().getString(R.string.menu_mono_capture)).setTabListener(this)
                   .setTag(Fragments.FRAGMENT_CAPTURE_MONO));
        bar.addTab(bar.newTab().setText(getResources().getString(R.string.menu_stereo_capture)).setTabListener(this)
                   .setTag(Fragments.FRAGMENT_CAPTURE_STEREO));
        bar.addTab(bar.newTab().setText(getResources().getString(R.string.menu_viewer)).setTabListener(this)
                   .setTag(Fragments.FRAGMENT_VIEWER));

        bar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_USE_LOGO);
        bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        bar.setDisplayShowHomeEnabled(true);
        //NATIVE CODE TEST 2
        //FCamInterface add = FCamInterface.GetInstance();
        //int result = add.PleaseAdd(5,10);
        //Log.d("DEBUG","PleaseAdd value: "+ result);
        
        Log.d("DEBUG", "Before calling getExposedBigImage");
        ArrayList<Bitmap> capturedBigImages = getExposedBigImages(mStorageDirectory); //original big images
      	Log.d("DEBUG", "Got the captured BIG images");
	    
	    if(capturedBigImages != null){ //will work only if images have been captured
	        double[] imageExposures = getExposureValues(mStorageDirectory);
	        Log.d("DEBUG", "Got the exposures of the big images");
	        int[][]ZrBig = readImagesRed(capturedBigImages);
	        Log.d("DEBUG", "Got the Zij reds of the big images");
	        
	        int[][]ZgBig = readImagesGreen(capturedBigImages);
	        Log.d("DEBUG", "Got the Zij greens of the big images");
	        int[][]ZbBig = readImagesBlue(capturedBigImages);
	        Log.d("DEBUG", "Got the Zij blue of the big images");
	    	int[][]Zr = new int[100][ZrBig[0].length]; //sample from Zij big but not from thumbnails anymore
	 		int[][]Zg = new int[100][ZgBig[0].length]; 
	 		int[][]Zb = new int[100][ZbBig[0].length]; 
			int scaler = ZrBig.length/100;
			for(int i = 0; i<Zr[0].length; i++){
				for (int j = 0; j<100; j++){
					Zr[j][i] = ZrBig[j*scaler][i];
			        //Log.d("DEBUG", "Got the Zij small reds of the images");
					Zg[j][i] = ZgBig[j*scaler][i];
			        //Log.d("DEBUG", "Got the Zij small greens of the images");
					Zb[j][i] = ZbBig[j*scaler][i];
			        //Log.d("DEBUG", "Got the Zij small blue of the  images");

				}
			}
	        float l = 10;
	        int[] w = getW();
	        Log.d("DEBUG", "Got the w");
	        double[]xR = gSolve(Zr, imageExposures, l, w);
	        Log.d("DEBUG", "Gsolve works for xR");
			double[]xG = gSolve(Zg, imageExposures, l, w);
	        Log.d("DEBUG", "Gsolve works for xG");
			double[]xB = gSolve(Zb, imageExposures, l, w);
	        Log.d("DEBUG", "Gsolve works for xB");
			double[]gR=getG(xR);
	        Log.d("DEBUG", "Got gr");
			double[]gG=getG(xG);
	        Log.d("DEBUG", "Got gG");
			double[]gB=getG(xB);
			Log.d("DEBUG", "Got gB");
//			double[]leR = getlE(gR);
//			double[]leG = getlE(gG);
//			double[]leB = getlE(gB);
			double[] Er = generateIrradianceChannel(gR, ZrBig, w, imageExposures);
			Log.d("DEBUG", "Generate Er");
			double[] Eg = generateIrradianceChannel(gG, ZgBig, w, imageExposures);
			Log.d("DEBUG", "Generate Eg");
			double[] Eb = generateIrradianceChannel(gB, ZbBig, w, imageExposures);
			Log.d("DEBUG", "Generate Eb");
//			//GrayScale
			double[]average = generateRadianceMapAverage(Er, Eg, Eb);
			Log.d("DEBUG", "Generate Eaverage");
			int []Enew = generateRadianceMapValues(average);
			Log.d("DEBUG", "Generate Enew");
			generateImage(Enew, Enew, Enew, "grayScaleRadianceMap.jpg");
			Log.d("DEBUG", "GrayScale Radiance Map created");
//			//Histogram EQ		
			int[] ErNew = generateRadianceMapValues(Er);
			int[] EgNew = generateRadianceMapValues(Eg);
			int[] EbNew = generateRadianceMapValues(Eb);
			Log.d("DEBUG", "Generate ErNew, EgNew, EbNew for histogram");
			int[] ErHisto = HistogramEQ(ErNew);
			int[] EgHisto = HistogramEQ(EgNew);
			int[] EbHisto = HistogramEQ(EbNew);	
			Log.d("DEBUG", "Generate ErHisto, EgHisto, EbHisto");
			generateHDRImage(ErHisto, EgHisto, EbHisto, "finalHDR.jpg");
			Log.d("DEBUG", "Final HDR Image created");
			
			//OLD
			//generateRadianceMapImage(Enew); //Generate grayscale
			//getErgbNew(Er, Eg, Eb);//generate the rgbnew (input for histogram)
	    }//END FOR IF
    }
    
    /*NATIVE CODE CALL
    native int Fibonacci ( int n );
    
    static {
    	System.loadLibrary("jni_fcamerapro");
    }*/
    
    /**
     * Called when tab is reselected.
     */
    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }

    /**
     * Called when a new tab is selected.
     */
    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        switch ((Fragments) tab.getTag()) {
        case FRAGMENT_CAPTURE_MONO:
            ft.replace(R.id.main_view, mCameraFragment);
            // TODO: check how to add support for `back' button
            if (ft.isAddToBackStackAllowed()) {
                ft.addToBackStack(null);
            }
            break;
        case FRAGMENT_CAPTURE_STEREO:
            ft.replace(R.id.main_view, mStereoCameraFragment);
            if (ft.isAddToBackStackAllowed()) {
                ft.addToBackStack(null);
            }
            break;
        case FRAGMENT_VIEWER:
            ft.replace(R.id.main_view, mViewerFragment);
            if (ft.isAddToBackStackAllowed()) {
                ft.addToBackStack(null);
            }
            break;
        }

        // switch menu
        invalidateOptionsMenu();
    }

    /**
     * Called when tab in unselected.
     */
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    }

    /**
     * Shows top-layer message box
     *
     * @param title
     *            defines the message box tile
     * @param text
     *            stores the message box text
     */
    public void showDialog(String title, String text) {
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        DialogFragment newFragment = Utils.InfoDialogFragment.NewInstance(title, text);
        newFragment.show(ft, "dialog");
    }

    /**
     * Loads fragment specific options menu (pull-down, top-right corner).
     */
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        switch ((Fragments) getActionBar().getSelectedTab().getTag()) {
        case FRAGMENT_CAPTURE_MONO:
            inflater.inflate(R.menu.camera_menu, menu);
            break;
        case FRAGMENT_CAPTURE_STEREO:
            inflater.inflate(R.menu.stereo_camera_menu, menu);
            break;
        case FRAGMENT_VIEWER:
            inflater.inflate(R.menu.viewer_menu, menu);
            break;
        }
        return true;
    }

    /**
     * Called when particular option menu is selected. Here we handle cases
     * common to option menus in all fragments, like "about".
     */
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.mi_about:
            showDialog(getResources().getString(R.string.menu_item_about), getResources().getString(R.string.copyright));
            return true;

        default:
            return super.onOptionsItemSelected(item);
        }
    }
    
    //GET THE THUMBNAILS NOT BEING USED    
    /*
    public ArrayList<Bitmap> getExposedThumbnailImages(String storageLocation){
    	Log.d("DEBUG", "Inside getexposedThumbnail");
    	ImageStackManager stackManager = new ImageStackManager(storageLocation);
    	Log.d("DEBUG", "After creating stackManager instance for thumbnails");
    	stackManager.refreshImageStacks();
    	Log.d("DEBUG", "After calling refresh ImageStack for thumbnails");
    	ImageStack stack = stackManager.getStack(0); //GET STACK 0 or 1st stack
    	if(stack==null){
    		Log.d("DEBUG", "No image stack for thumbnails");
    	}
    	int imageCount = stack.getImageCount();
    	Log.d("DEBUG", "Thumbnail image count "+imageCount);
    	if(imageCount!=0){
    		Log.d("DEBUG", "Entered if");
	    	ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();
	    	Log.d("DEBUG", "Made bitmaps array list");
	    	for(int i = 0; i<imageCount; i++){
	    		//bitmaps.add(stack.getImage(i).getThumbnail()); //thumbnail picture
	    		Bitmap bMap = stack.getImage(i).getThumbnail();
	    		if(bMap == null){
	    			BitmapFactory.Options opts = new BitmapFactory.Options();
		            opts.inScaled = false;
		            String mThumbnailName = stack.getImage(i).getThumbnailName();
		            bMap = BitmapFactory.decodeFile(mThumbnailName, opts);
		            Log.d("DEBUG", "null thumbnail "+i);
	    		}
	    		//Log.d("DEBUG", "Got a thumbnail");
	    		//Bitmap resized = Bitmap.createScaledBitmap(bMap, 30, 20, false);
	    		//Log.d("DEBUG", "Resized the thumbnail");
	    		//bitmaps.add(resized);
	    		bitmaps.add(bMap);
	    		Log.d("DEBUG", "Thumbnail added to bitmaps arraylist");
	    	}
	    	return bitmaps;
    	}
    	else{
    		return null;
    	}
    	
    }
    */ 
    //GET THUMBNAILS NOT BEING USED   
//    public ArrayList<Bitmap> getExposedImages(String storageLocation){
//    	//storageLocation = mStorageDirectory;
//    	ImageStackManager stackManager = new ImageStackManager(storageLocation);
//    	ImageStack stack = stackManager.getStack(0); //GET STACK 0 or 1st stack
//    	int imageCount = stack.getImageCount();
//    	if(imageCount!=0){
//	    	ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();
//	    	for(int i = 0; i<imageCount; i++){
//	    		bitmaps.add(stack.getImage(i).getThumbnail()); //thumbnail picture
//	    	}
//	    	return bitmaps;
//    	}
//    	else{
//    		return null;
//    	}
//    	
//    }
    
    //GET THE ORIGINAL BIG IMAGES ONLY
    public ArrayList<Bitmap> getExposedBigImages(String storageLocation){
        //public ArrayList<Bitmap> getExposedBigImages(ImageStackManager mStack){
        	//storageLocation = mStorageDirectory;
        	Log.d("DEBUG", "Inside getExposedBigImage");
        	ImageStackManager stackManager = new ImageStackManager(storageLocation);
        	//ImageStackManager imageStackManager = mStack;
        	Log.d("DEBUG", "After creating stackManager instance");
        	stackManager.refreshImageStacks();
        	Log.d("DEBUG", "After calling refresh ImageStack");
        	ImageStack stack = stackManager.getStack(0); //GET STACK 0 or 1st stack
        	if(stack==null){
        		Log.d("DEBUG", "null stack");
        	}
        	//Log.d("DEBUG", "I got stack0");
        	int imageCount = stack.getImageCount();
        	Log.d("DEBUG", "imagecount "+imageCount);
        	if(imageCount!=0){
    	    	ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();
    	    	Log.d("DEBUG", "Made bitmaps arraylist");
    	    	for(int i = 0; i<imageCount; i++){
    	    		Image im = stack.getImage(i); //original sized picture
    	    		Log.d("DEBUG", "Got an image");
    				try {
    					Log.d("DEBUG", "Entered try");
    					FileInputStream in = new FileInputStream(new File(im.getName())); //gets the image file name to create fileinputstream
    					BufferedInputStream buf = new BufferedInputStream(in);
    		    		byte[] bMapArray= new byte[buf.available()];
    		    		Log.d("DEBUG", "Made a byte array");
    		    		buf.read(bMapArray);
    		    		Log.d("DEBUG", "Did reading");
    		    		BitmapFactory.Options o = new BitmapFactory.Options();
    		    		o.inSampleSize = 10;
    		    		Bitmap bMap = BitmapFactory.decodeByteArray(bMapArray, 0, bMapArray.length, o);
    		    		//Bitmap bMap = BitmapFactory.decodeByteArray(bMapArray, 0, bMapArray.length);
    		    		Log.d("DEBUG", "Got a Bitmap");
    		    		//Bitmap resized = Bitmap.createScaledBitmap(bMap, 600, 400, true);
    		    		//Log.d("DEBUG", "Got an resized Bitmap");
    		    		//bitmaps.add(resized);
    		    		bitmaps.add(bMap);
    		    		Log.d("DEBUG", "Bitmaps arraylist added a bitmap");
    		    		buf.close();
    				} catch (Exception e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				} 
    	    		
    	    	}
    	    	return bitmaps;
        	}
        	else{
        		return null;
        	}
        	
        }
    
    	//OLD    
//    public ArrayList<Bitmap> getExposedBigImages(String storageLocation){
//    	//storageLocation = mStorageDirectory;
//    	Log.d("DEBUG", "Inside getExposedBigImage");
//    	ImageStackManager stackManager = new ImageStackManager(storageLocation);
//    	Log.d("DEBUG", "After creating stackManager instance");
//    	ImageStack stack = stackManager.getStack(1); //GET STACK 0 or 1st stack
//    	Log.d("DEBUG", "I got stack0");
//    	int imageCount = stack.getImageCount();
//    	Log.d("DEBUG", "imagecount "+imageCount);
//    	if(imageCount!=0){
//	    	ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();
//	    	for(int i = 0; i<imageCount; i++){
//	    		Image im = stack.getImage(i); //original sized picture
//				try {
//					FileInputStream in = new FileInputStream(new File(im.getName())); //gets the image file name to create fileinputstream
//					BufferedInputStream buf = new BufferedInputStream(in);
//		    		byte[] bMapArray= new byte[buf.available()];
//		    		buf.read(bMapArray);
//		    		Bitmap bMap = BitmapFactory.decodeByteArray(bMapArray, 0, bMapArray.length);
//		    		bitmaps.add(bMap);
//		    		buf.close();
//				} catch (Exception e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				} 
//	    		
//	    	}
//	    	return bitmaps;
//    	}
//    	else{
//    		return null;
//    	}
//    	
//    }
    
    //GET EXPOSURE VALUES FROM BIG IMAGES
    public double[] getExposureValues(String storageLocation){
    	ImageStackManager stackManager = new ImageStackManager(storageLocation);
    	Log.d("DEBUG", "After creating stackManager instance");
    	stackManager.refreshImageStacks();
    	Log.d("DEBUG", "After calling refresh ImageStack");
    	ImageStack stack = stackManager.getStack(0);
    	if(stack==null){
    		Log.d("DEBUG", "Did not a stack0");
    	}
    	int imageCount = stack.getImageCount();
    	Log.d("DEBUG", "imagecount: " + imageCount);
    	double [] exposures = new double[imageCount];
    	double [] finalExposures = new double[imageCount];
    	for(int i = 0; i<imageCount; i++){
    		//Log.d("DEBUG", "Entered if");
    		exposures[i] = (double)stack.getImage(i).getExposure(); //got each exposure value as int in milliseconds and converted to double
    		//Log.d("DEBUG", "Got each exposure value");
    		finalExposures[i] = Math.log(1.0/(exposures[i]));
    		//Log.d("DEBUG", "Got final exposure value converted");
    	}
    	Log.d("DEBUG", "Got exposures array");
    	return finalExposures;
    }
    
    /*
    public double[] getExposureValues(String storageLocation){
    	ImageStackManager stackManager = new ImageStackManager(storageLocation);
    	ImageStack stack = stackManager.getStack(0);
    	int imageCount = stack.getImageCount();
    	double [] exposures = new double[imageCount];
    	double [] finalExposures = new double[imageCount];
    	for(int i = 0; i<imageCount; i++){
    		exposures[i] = (double)stack.getImage(i).getExposure(); //got each exposure value as int in milliseconds and converted to double
    		finalExposures[i] = Math.log(1.0/(exposures[i]));
    	}

    	return finalExposures;
    }
    */
    
    //NEW COMBINED METHOD NOT BEING USED    
    /*
    public int[][]readImagesRed(String storageLocation){
    	ImageStackManager stackManager = new ImageStackManager(storageLocation);
    	Log.d("DEBUG", "After creating stackManager instance");
    	stackManager.refreshImageStacks();
    	Log.d("DEBUG", "After calling refresh ImageStack");
    	ImageStack stack = stackManager.getStack(0); //GET STACK 0 or 1st stack
    	Log.d("DEBUG", "Stack instance");
    	int imageCount = stack.getImageCount();
    	Log.d("DEBUG", "Thumbnail image count "+imageCount);
    	ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>();
    	Log.d("DEBUG", "Made bitmaps array list");
    	if(imageCount!=0){
    		Log.d("DEBUG", "Entered if");
	    	for(int i = 0; i<imageCount; i++){
	    		//bitmaps.add(stack.getImage(i).getThumbnail()); //thumbnail picture
	    		Bitmap bMap = stack.getImage(i).getThumbnail();
	    		Log.d("DEBUG", "Got a thumbnail");
	    		//Bitmap resized = Bitmap.createScaledBitmap(bMap, 30, 20, false);
	    		//Log.d("DEBUG", "Resized the thumbnail");
	    		//bitmaps.add(resized);
	    		bitmaps.add(bMap);
	    		Log.d("DEBUG", "Thumbnail added to bitmaps arraylist");
	    	}
	    	int imageHeight = bitmaps.get(1).getHeight(); //height of 1 image
	    	Log.d("DEBUG", "Got imageHeight");
	    	int imageWidth = bitmaps.get(1).getWidth(); //width of 1 image
	    	Log.d("DEBUG", "Got imageWidth");
	    	int Z[][] = new int[imageHeight*imageWidth][bitmaps.size()];
	    	Log.d("DEBUG", "made zij");
	    	int location = 0;
	    	for (int i=0; i<bitmaps.size(); i++) //consider looping on 6 images
	        {
	         location =0;//Reset to 0 after one image is completed.
	         Log.d("DEBUG", "entered i=0");
	         for (int j=0; j<imageHeight; j++) // Consider looping of all rows for the current image
	         {
	        	 Log.d("DEBUG", "entered j=0");
	          for (int k=0; k<imageWidth; k++)//Consider looping on all columns
	          {
	               //The TYPE_INT_ARGB represents Color as an int (4 bytes) with alpha channel in bits 24-31, 
	               //red channels in 16-23, green in 8-15 and blue in 0-7.
	               //int rgb = rgbColor.getRGB(); //always returns TYPE_INT_ARGB
	        	  //int alpha = (rgb >> 24) & 0xFF;
	        	  //int red =   (rgb >> 16) & 0xFF;
	        	  // int green = (rgb >>  8) & 0xFF;
	              //int blue =  (rgb) & 0xFF;
	        	  Log.d("DEBUG", "Entered k");
	              int redValue = ((bitmaps.get(i).getPixel(k, j))>>16) & 0xFF;//index of location is j,k as TYPE_INT_ARGB
	              Log.d("DEBUG", "redValue "+redValue);
	              //int greenValue = ((imageArray[i].getRGB(k,j])>> 8) & 0xFF;//index of location is j,k as TYPE_INT_ARGB
	              //int blueValue = (imageArray[i].getRGB(k,j]) & 0xFF;//index of location is j,k as TYPE_INT_ARGB
	              Z[location][i] = redValue;
	              Log.d("DEBUG", "put into Z");
	              //Z[location][i] = greenValue;
	              //Z[location][i] = blueValue;
	              location++;  //creates multiplication of rows and columns indirectly
	              Log.d("DEBUG", "Finished k loop");
	          }
	         
	         }
	        
	        }
	    	return Z;
	    	
    	}
    	else{
    		return null;
    	}
    }
    */
    
    
    // New NOT WORKING
    /*
    public int [][]readImagesRed(ArrayList<Bitmap> bits){
    	//int imageHeight = bits.get(0).getHeight(); //height of 1 image
    	int imageHeight = 288;
    	Log.d("DEBUG", "Got imageHeight");
    	//int imageWidth = bits.get(0).getWidth(); //width of 1 image
    	int imageWidth = 384;
    	Log.d("DEBUG", "Got imageWidth");
    	int Z[][] = new int[imageHeight*imageWidth][bits.size()];
    	Log.d("DEBUG", "made zij");
    	for (int i=0; i<bits.size(); i++) //consider looping on 6 images
        {
	         Log.d("DEBUG", "entered i=0");
	         int[]buffer = new int[imageWidth*imageHeight];
	         Log.d("DEBUG", "made buffer");
	   	  	 (bits.get(i)).getPixels(buffer, 0, imageWidth, 0, 0, imageWidth, imageHeight);
	   	  	 Log.d("DEBUG", "got the pixels");
	   	  	 for (int m =0; m<buffer.length; m++){
	   	  		Log.d("DEBUG", "entered m loop");
	   	  		int pixel = buffer[m];
	   	  		Log.d("DEBUG", "got a pixel");
	   	  		int redValue=(pixel >> 16) & 0xFF;
	   	  		Log.d("DEBUG", "redValue "+redValue);
	   	  		Z[m][i] = redValue;
	   	  		Log.d("DEBUG", "put into Z");
	   	  	 }
        
        }
    	return Z;
    }
    */
    
    //Original
    
    //Passing in the big images arraylist
    //Generating Zij big
    public int [][]readImagesRed(ArrayList<Bitmap> bits){
    	//READS IN ARRAYLIST OF THUMBNAILS
    	//Log.d("DEBUG", "Inside readImagesRed");
    	if(bits.get(0)==null){
    		//Log.d("DEBUG", "this bitmap is null");
    	}
    	int imageHeight = bits.get(0).getHeight(); //height of 1 image
    	Log.d("DEBUG", "imageHeight "+imageHeight);
    	//int imageHeight = 288;
    	//int imageHeight = 384;
    	int imageWidth = bits.get(0).getWidth(); //width of 1 image
    	//int imageWidth = 384;
    	//int imageWidth = 288;
    	int Z[][] = new int[imageHeight*imageWidth][bits.size()];
    	//Log.d("DEBUG", "made zij");
    	int location = 0;
    	for (int i=0; i<bits.size(); i++) //consider looping on 6 images
        {
    	 //Log.d("DEBUG", "inside i loop");
         location =0;//Reset to 0 after one image is completed.
         for (int j=0; j<imageHeight; j++) // Consider looping of all rows for the current image
         {
           //Log.d("DEBUG", "inside j loop");
          for (int k=0; k<imageWidth; k++)//Consider looping on all columns
          {
        	  //Log.d("DEBUG", "inside k loop");
               //The TYPE_INT_ARGB represents Color as an int (4 bytes) with alpha channel in bits 24-31, 
               //red channels in 16-23, green in 8-15 and blue in 0-7.
               //int rgb = rgbColor.getRGB(); //always returns TYPE_INT_ARGB
        	  //int alpha = (rgb >> 24) & 0xFF;
        	  //int red =   (rgb >> 16) & 0xFF;
        	  // int green = (rgb >>  8) & 0xFF;
              //int blue =  (rgb) & 0xFF;
        	  //Log.d("DEBUG", "before redvalue");
              //int redValue = ((bits.get(i).getPixel(k, j))>>16) & 0xFF;//index of location is j,k as TYPE_INT_ARGB
        	  Bitmap bMap = bits.get(i);
        	  if(bMap == null){
        		  //Log.d("DEBUG", "got a null bitmap");
        	  }
        	  //Log.d("DEBUG", "Got a bitmap");
        	  int pixel = bMap.getPixel(k,j);
        	  //Log.d("DEBUG", "got pixel");
        	  int redValue = (pixel >> 16) & 0xFF;
        	  //Log.d("DEBUG", "got redValue");
              Z[location][i] = redValue;
              //Log.d("DEBUG", "finished zij");
              location++;  //creates multiplication of rows and columns indirectly
          
          }
         
         }
        
        }
    	return Z;
    }
    //Zij green for big
    public int [][]readImagesGreen(ArrayList<Bitmap> bits){
    	int imageHeight = bits.get(0).getHeight(); //height of 1 image
    	int imageWidth = bits.get(0).getWidth(); //width of 1 image
    	int Z[][] = new int[imageHeight*imageWidth][bits.size()];
    	int location = 0;
    	for (int i=0; i<bits.size(); i++) //consider looping on 6 images
        {
         location =0;//Reset to 0 after one image is completed.
         for (int j=0; j<imageHeight; j++) // Consider looping of all rows for the current image
         {
          for (int k=0; k<imageWidth; k++)//Consider looping on all columns
          {
               //The TYPE_INT_ARGB represents Color as an int (4 bytes) with alpha channel in bits 24-31, 
               //red channels in 16-23, green in 8-15 and blue in 0-7.
               //int rgb = rgbColor.getRGB(); //always returns TYPE_INT_ARGB
        	  //int alpha = (rgb >> 24) & 0xFF;
        	  //int red =   (rgb >> 16) & 0xFF;
        	  // int green = (rgb >>  8) & 0xFF;
              //int blue =  (rgb) & 0xFF;

              int greenValue = ((bits.get(i).getPixel(k, j))>>8) & 0xFF;//index of location is j,k as TYPE_INT_ARGB
              Z[location][i] = greenValue;
              location++;  //creates multiplication of rows and columns indirectly
          
          }
         
         }
        
        }
    	return Z;
    }
    //Zij blue big
    public int [][]readImagesBlue(ArrayList<Bitmap> bits){
    	int imageHeight = bits.get(0).getHeight(); //height of 1 image
    	int imageWidth = bits.get(0).getWidth(); //width of 1 image
    	int Z[][] = new int[imageHeight*imageWidth][bits.size()];
    	int location = 0;
    	for (int i=0; i<bits.size(); i++) //consider looping on 6 images
        {
         location =0;//Reset to 0 after one image is completed.
         for (int j=0; j<imageHeight; j++) // Consider looping of all rows for the current image
         {
          for (int k=0; k<imageWidth; k++)//Consider looping on all columns
          {
               //The TYPE_INT_ARGB represents Color as an int (4 bytes) with alpha channel in bits 24-31, 
               //red channels in 16-23, green in 8-15 and blue in 0-7.
               //int rgb = rgbColor.getRGB(); //always returns TYPE_INT_ARGB
        	  //int alpha = (rgb >> 24) & 0xFF;
        	  //int red =   (rgb >> 16) & 0xFF;
        	  // int green = (rgb >>  8) & 0xFF;
              //int blue =  (rgb) & 0xFF;

              int blueValue = ((bits.get(i).getPixel(k, j))) & 0xFF;//index of location is j,k as TYPE_INT_ARGB
              Z[location][i] = blueValue;
              location++;  //creates multiplication of rows and columns indirectly
          
          }
         
         }
        
        }
    	return Z;
    }
    
    public int[] getW(){
		int Zmax = 255; //n
		int Zmin = 0;
		int n = 256;
		int[] w = new int[n];
		
		
		for(int z=0; z<n; z++){ //256 = ZMax
	      		if (z<= (1/2*(Zmin + Zmax))){
	             	 	w[z] = z-Zmin;
	     		 }	
	     	 	else if (z> (1/2*(Zmin + Zmax))){
	              		w[z] = Zmax -z;
	    		 }   
		}
		return w;

	}
    
    public double[] gSolve(int[][] Z, double[] B, float l, int[] w){
		
		int n = 256;
		int numOfZrows = Z.length;
		int numOfZcolumns = Z[0].length;

		//declare A, b, x
		double[][] A = new double[numOfZrows*numOfZcolumns+n+1][n+numOfZrows];
		double[][] b = new double[numOfZrows*numOfZcolumns+n+1][1];
		double[] x;
		
		//Include the data-fitting equations
		int k = 0;
		int i;
		int j;
		double wij;
		for (i=0; i<numOfZrows; i++){
			for (j=0; j<numOfZcolumns; j++){
				wij = (double)w[Z[i][j]];
				A[k][(Z[i][j])] = wij;
				A[k][n+i] = -wij;
				b[k][0] = wij * B[j];
				k++;
			}
		}
		
		//Fix the curve by setting its middle value to 0
		A[k][128] = 1;
		k = k + 1;	
		
		//Include the smoothness equations
		for (i=0; i<(n-2); i++){
			A[k][i] = (double)l * w[i+1];
			A[k][i+1] = (double)(-2)* l * w[i+1];
			A[k][i+2] = (double)l * w[i+1];
			k = k + 1;
		}
		
		//Convert A,b to Mat
		Matrix MatA = new Matrix(A);
		Matrix Matb = new Matrix(b);
		
		//Solve Ax = b
		SingularValueDecomposition Asvd = new SingularValueDecomposition (MatA);
		
		Matrix MatU = Asvd.getU();
		MatU = MatU.transpose();
		
		Matrix MatS = Asvd.getS();
		for (int r = 0; r < Asvd.rank(); r++){
			MatS.set(r, r, 1.0/(MatS.get(r,r)));
		}
		
		Matrix MatV = Asvd.getV();

		Matrix MatX = MatV.times(MatS).times(MatU).times(Matb);	

		//Return x as double[]
		x = MatX.transpose().getArrayCopy()[0];
		return x;
	}
	
	public double[] getG(double[] x){
		int n = 256;
		double[] g = new double[n];
		for (int i=0; i<n; i++){
			g[i] = x[i];
		}
		return g;
	}
	
	public double[] getlE(double[] x){
		int n = 256;
		double[] lE = new double[x.length-n];
		for (int i=0; i<(x.length-n); i++){
			lE[i] = x[i+n]; 
		}
		return lE;
	}
	
	public double[] generateIrradianceChannel(double[]g, int[][]Z, int[]w, double[]B){
	    int size = Z.length; //Z[i][j] and size is total value for i
	    double[] result = new double[size];
	    for (int i = 0; i<size; i++){ //1st pixel location i
	        double sumOne = 0.0;
	        double sumTwo = 0.0;
	        double sum;
	        for (int j = 0; j<6; j++){
	            int pixel = Z[i][j]; //grabbing pixel value (0-255) for 1 rgb channel at this part of the array
	            sum = w[pixel]*(g[pixel]-B[j]);
	            sumOne+=sum;
	            sumTwo+=w[pixel];
	        }
	        result[i]= sumOne/sumTwo;
	    }
	    return result; //Er, Eg, Eb separately
	}
	public double[] generateRadianceMapAverage(double []Er, double []Eg, double []Eb){
	    int size = Er.length;
	    double [] Eaverage = new double[size];
	    double value1 = 0.0;
	    double value2 = 0.0;
	    double value3 = 0.0;
	    double average= 0.0;
	    for(int i = 0; i<size; i++){
	        value1 = Er[i];
	        value2 = Eg[i];
	        value3 = Eb[i];
	        average=(value1+value2+value3)/3;
	        Eaverage[i]=average; //double[]
	    }
	    return Eaverage; //average
	}
	public int[] generateRadianceMapValues(double []Eaverage){
		int size = Eaverage.length;
	    int [] Enew= new int[size];
	    //calculate min value of Eresult
    	double min = Eaverage[0]; //the minimum is the first value initially
	    for (int i = 1; i<size; i++){
	    	if(min>Eaverage[i]){
	    		min = Eaverage[i];
	    	}
	    }
	    //calculate max value of Ereult
	    double max = Eaverage[0];
	    for(int i = 0; i<size; i++){
	    	if(max<Eaverage[i]){
	    		max = Eaverage[i];
	    	}
	    }
	    //calculate scalar
	    double scalar = 255.0/(max-min);
	    for (int j = 0; j<size; j++){
	    	Enew[j]=(int) ((Eaverage[j]-min)*scalar);
	    	//Log.d("DEBUG", "Enew value: "+Enew[j]); //should be 0-255 range
	    }
	    
		return Enew;
		
	}
	
	public void generateImage(int[] rArray, int[] gArray, int[] bArray, String fileName){
		Bitmap output = Bitmap.createBitmap(320, 240, Bitmap.Config.ARGB_8888); //creates an empty bitmap
		Log.d("DEBUG", "Generate bitmap of smaller size");
	    int i = 0;
	    for(int y = 0; y<output.getHeight(); y++){
	    
	        for (int x = 0; x<output.getWidth(); x++){
	    	    //for(int y = 0; y<output.getHeight(); y++){

		            int red = rArray[i];
		            int green = gArray[i];
		            int blue = bArray[i];
		            int newPixel = 255; newPixel = newPixel << 8;
		            newPixel += (255-red); newPixel = newPixel << 8;
		            newPixel += (255-green); newPixel = newPixel << 8;
		            newPixel += (255-blue);
		            output.setPixel(x,y,newPixel);
		            i++;
		            //Log.d("DEBUG", "Added the new pixels");
	    	}
	    }
	    try{
			String name = fileName;
			String folderPath = "/mnt/sdcard/DCIM/fcam/radianceMapGray";
			File folder = new File(folderPath);
			folder.mkdir();
			Log.d("DEBUG", "Make a new directory for grayscale radiance map");
			File radianceMapGray = new File(folder, name);

			//Convert bitmap to byte array
			Bitmap bitmap = output;
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			bitmap.compress(CompressFormat.JPEG, 100, bos); //MIGHT HAVE TO CHANGE TO JPEG
			Log.d("DEBUG", "Bitmap compressed");
			byte[] bitmapdata = bos.toByteArray();
			Log.d("DEBUG", "Byte array created");

			//write the bytes in file
			FileOutputStream fos = new FileOutputStream(radianceMapGray);
			fos.write(bitmapdata);
			Log.d("DEBUG", "Bytes written to file");
			fos.flush();
			fos.close();
			
		}catch (Exception e) {
            e.printStackTrace();
        }
	    
	}
	//Histogram Equalization
	public int[] HistogramEQ(int[] ENew){
		
	    int[] histo = new int[256];
	    for(int i = 0; i < 256; i++){
	    	histo[i] = 0;
	    }
	
	    for(int i = 0; i < ENew.length; i++){
	    	histo[ENew[i]]++;
	    }
	
	    for(int i = 1; i < 256; i++){
	    	histo[i] = (histo[i]+histo[i-1]);
	    }
	
	    float scale_factor = (float) (255.0 / ENew.length);
	    for(int i = 0; i < 256; i++){
	    	histo[i] = (int)(histo[i]*scale_factor);
	    }
	
	    for(int i = 0; i < ENew.length; i++){
	         ENew[i] = histo[ENew[i]];
	    }    
	
	    return ENew;
	}
	//Generate HDR image
	public void generateHDRImage(int[] rArray, int[] gArray, int[] bArray, String fileName){
		Bitmap output = Bitmap.createBitmap(320, 240, Bitmap.Config.ARGB_8888); //creates an empty bitmap
	    int i = 0;
	    for(int y = 0; y<output.getHeight(); y++){
	        for (int x = 0; x<output.getWidth(); x++){
	            int red = rArray[i];
	            int green = gArray[i];
	            int blue = bArray[i];
	            int newPixel = 255; newPixel = newPixel << 8;
	            newPixel += (255-red); newPixel = newPixel << 8;
	            newPixel += (255-green); newPixel = newPixel << 8;
	            newPixel += (255-blue);
	            output.setPixel(x,y,newPixel);
	            i++;
	        }
	    }
	    try{
			String name = fileName;
			String folderPath = "/mnt/sdcard/DCIM/fcam/HDRImage";
			File folder = new File(folderPath);
			folder.mkdir();
			File HDRImage = new File(folder, name);

			//Convert bitmap to byte array
			Bitmap bitmap = output;
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			bitmap.compress(CompressFormat.JPEG, 100, bos); //MIGHT HAVE TO CHANGE TO JPEG
			byte[] bitmapdata = bos.toByteArray();

			//write the bytes in file
			FileOutputStream fos = new FileOutputStream(HDRImage);
			fos.write(bitmapdata);
			fos.flush();
			fos.close();
			
		}catch (Exception e) {
            e.printStackTrace();
        }
	    
	}
	
	//OLD CODE
		
	//Radiance Map Gray
//	public void generateRadianceMapImage(int[] Eresult){
//		Bitmap output = Bitmap.createBitmap(2560, 1920, Bitmap.Config.ARGB_8888); //creates an empty bitmap
//        int i = 0;
//        for (int y = 0; y<output.getHeight(); y++){
//            for(int x = 0; x<output.getWidth(); x++){
//                int pixel = Eresult[i]; //try to cast it to char if needed
//                pixel = pixel<<8;
//                pixel = pixel + Eresult[i];
//                pixel = pixel<<8;
//                pixel = pixel + Eresult[i];
//                output.setPixel(x, y, pixel);
//                i++;
//            }
//        }
//		try{
//			String name="grayScale";
//			String folderPath = "/mnt/sdcard/DCIM/fcam/radianceMapGray";
//			File folder = new File(folderPath);
//			folder.mkdir();
//			File radianceMapGray = new File(folder, name);
//
//			//Convert bitmap to byte array
//			Bitmap bitmap = output;
//			ByteArrayOutputStream bos = new ByteArrayOutputStream();
//			bitmap.compress(CompressFormat.PNG, 0, bos); //MIGHT HAVE TO CHANGE TO JPEG
//			byte[] bitmapdata = bos.toByteArray();
//
//			//write the bytes in file
//			FileOutputStream fos = new FileOutputStream(radianceMapGray);
//			fos.write(bitmapdata);
//			fos.flush();
//			fos.close();
//			
//		}catch (Exception e) {
//            e.printStackTrace();
//        }
//	}
	
	//Radiance Map ONLY
//	public void getErgbNew(double[] Er, double[] Eg, double[] Eb){
//	    int[] ErNew = generateRadianceMapValues(Er);
//	    int[] EgNew = generateRadianceMapValues(Eg);
//	    int[] EbNew = generateRadianceMapValues(Eb);
//	    Bitmap rgbNew = Bitmap.createBitmap(2560, 1920, Bitmap.Config.ARGB_8888); //creates an empty bitmap
//	    int i = 0;
//	    for(int y = 0; y<rgbNew.getHeight(); y++){
//	        for (int x = 0; x<rgbNew.getWidth(); x++){
//	            int red = ErNew[i];
//	            int green = EgNew[i];
//	            int blue = EbNew[i];
//	            int newPixel = 0;
//	            newPixel += red; newPixel = newPixel << 8;
//	            newPixel += green; newPixel = newPixel << 8;
//	            newPixel += blue;
//	            rgbNew.setPixel(x,y,newPixel);
//	            i++;
//	        }
//	    }
//	    try{
//			String name="rgbNew";
//			String folderPath = "/mnt/sdcard/DCIM/fcam/radianceMap";
//			File folder = new File(folderPath);
//			folder.mkdir();
//			File radianceMap = new File(folder, name);
//
//			//Convert bitmap to byte array
//			Bitmap bitmap = rgbNew;
//			ByteArrayOutputStream bos = new ByteArrayOutputStream();
//			bitmap.compress(CompressFormat.PNG, 0, bos); //MIGHT HAVE TO CHANGE TO JPEG
//			byte[] bitmapdata = bos.toByteArray();
//
//			//write the bytes in file
//			FileOutputStream fos = new FileOutputStream(radianceMap);
//			fos.write(bitmapdata);
//			fos.flush();
//			fos.close();
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//	}
}
